package icedot.app.android.nativeso;

import android.content.ComponentName;
import android.os.Message;
import android.util.Log;

import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import icedot.app.android.nativeso.okhttp.ProgressHelper;
import icedot.app.android.nativeso.okhttp.UIProgressListener;
import icedot.library.common.base.Logger;
import icedot.library.common.utils.CommonDef;
import icedot.library.common.utils.Md5;
import icedot.library.common.utils.StringUtils;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

/**
 * Created by IceDot on 2016/9/5.
 */
public class OkHttpManager implements NativeCallback
{
    //    private class NotifyInfo
    //    {
    //        NetworkNotify _notify;
    //        String _url;
    //    }

    private NativeHandler _handler;
    private Map<String, NetworkNotify> _notifyList = new ConcurrentHashMap<>();
    private Map<String, List<NetworkNotify>> _fileList = new ConcurrentHashMap<>();
    private OkHttpClient _client;

    public OkHttpManager()
    {

        _client = new OkHttpClient().newBuilder().connectTimeout(15, TimeUnit.SECONDS).
                readTimeout(15, TimeUnit.SECONDS).build();
        _handler = new NativeHandler(this);
        _notifyList = new HashMap<>();
    }

    public OkHttpManager(int timeOut)
    {
        _client = new OkHttpClient().newBuilder().connectTimeout(timeOut, TimeUnit.SECONDS).
                readTimeout(timeOut, TimeUnit.SECONDS).build();
        _handler = new NativeHandler(this);
        _notifyList = new HashMap<>();
    }

    public OkHttpClient getHttpClient()
    {
        return _client;
    }

    @Override
    public void recvCallback(String flag, int type, byte[] content)
    {
        if (_notifyList.containsKey(flag))
        {
            NetworkNotify info = _notifyList.get(flag);
            if (info == null)
            {
                Logger.showDebugMsg(Logger.s_error, "Exception", "NetworkNotify not find");
                return;
            }
            notify(info, flag, type, content);
            _notifyList.remove(flag);
            Logger.showDebugMsg("remove flag " + flag);
        }
        else if (_fileList.containsKey(flag))
        {
            List<NetworkNotify> list = _fileList.get(flag);
            for (NetworkNotify item : list)
            {
                notify(item, flag, type, content);
            }
            _fileList.remove(flag);
            Logger.showDebugMsg("remove flag " + flag);
        }
        else
        {
            Logger.errorMsg(flag + " not find");
        }
    }

    private void notify(NetworkNotify notify, String flag, int type, byte[] content)
    {
        try
        {
            if (type == NativeClient.s_httpSuccess || type == NativeClient.s_httpProcess)
            {
                notify.onSuccess(type, content);
            }
            else if (type == NativeClient.s_httpError)
            {
                String ret = new String(content, "UTF-8");
                notify.onNoNetwork(type, ret.toString());
//                if (CommonDef.s_debug)
//                {
//                    notify.onNoNetwork(type, ret.toString());
//                }
//                else
//                {
//                    ret = "获取数据异常,请重试...";
                    notify.onNoNetwork(type, ret.toString());
//                }
                Logger.errorMsg("flag=" + flag + " type=" + type + " POST<--" + ret.toString());
            }
            else if (type == NativeClient.s_noNetwork)
            {
                String ret = new String(content, "UTF-8");
                notify.onNoNetwork(type, ret.toString());
                if (CommonDef.s_debug)
                {
                    Logger.errorMsg("flag=" + flag + " type=" + type + " POST<--" + ret.toString());
                }
            }
        }
        catch (Exception e)
        {
            Logger.showDebugMsg(Logger.s_error, "Exception", e.getMessage());
        }
    }

    public void initLongConnect(String ip, int port, String logPath)
    {

    }

    public void httpPost(String url, String head, String content, NetworkNotify notify)
    {
        String flag = NetworkManager.getFlags();
        _notifyList.put(flag, notify);
        Request.Builder builder = new Request.Builder().url(url);

        try
        {
            JSONObject headObj = new JSONObject(head);
            Iterator<String> keys = headObj.keys();
            while (keys.hasNext())
            {
                String key = keys.next();
                String value = headObj.getString(key);
                builder.header(key, value);
            }
            RequestBody formBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), content);
            builder.post(formBody);
        }
        catch (Exception e)
        {
            Logger.errorMsg(e.getMessage());
        }
        Request request = builder.build();
        final Call call = _client.newCall(request);
        asyncRequest(call, flag, notify);
        if (CommonDef.s_debug)
        {
            Logger.showDebugMsg("okhttp", "flag=" + flag + " send " + url);
        }
    }

    public void httpPost(String url, String content, NetworkNotify notify)
    {
        String flag = NetworkManager.getFlags();
        _notifyList.put(flag, notify);
        Request.Builder builder = new Request.Builder().url(url);
        try
        {
            JSONObject jsonObj = new JSONObject(content);
            if (!jsonObj.has("head"))
            {
                return;
            }
            JSONObject headObj = jsonObj.getJSONObject("head");
            Iterator<String> keys = headObj.keys();
            while (keys.hasNext())
            {
                String key = keys.next();
                String value = headObj.getString(key);
                builder.header(key, value);
            }
            RequestBody formBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonObj.getString("content"));
            builder.post(formBody);
        }
        catch (Exception e)
        {
            Logger.errorMsg(e.getMessage());
        }
        Request request = builder.build();
        final Call call = _client.newCall(request);
        asyncRequest(call, flag, notify);
        if (CommonDef.s_debug)
        {
            Logger.showDebugMsg("okhttp", "flag=" + flag + " send " + url);
        }
    }

    public void httpPut(final String url,final String content,final NetworkNotify notify)
    {
        String flag = NetworkManager.getFlags();
        _notifyList.put(flag, notify);
        Request.Builder builder = new Request.Builder().url(url);
        try
        {
            JSONObject jsonObj = new JSONObject(content);
            if (!jsonObj.has("head"))
            {
                return;
            }
            JSONObject headObj = jsonObj.getJSONObject("head");
            Iterator<String> keys = headObj.keys();
            while (keys.hasNext())
            {
                String key = keys.next();
                String value = headObj.getString(key);
                builder.header(key, value);
            }
            RequestBody formBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonObj.getString("content"));
            builder.put(formBody);
        }
        catch (Exception e)
        {
            Logger.errorMsg(e.getMessage());
        }
        Request request = builder.build();
        final Call call = _client.newCall(request);
        asyncRequest(call, flag, notify);
        if (CommonDef.s_debug)
        {
            Logger.showDebugMsg("okhttp", "url=" + url + " flag=" + flag + " send " + url);
        }
    }

    public void httpGet(final String url, final String content, final NetworkNotify notify)
    {
        String flag = NetworkManager.getFlags();
        _notifyList.put(flag, notify);
        Request.Builder builder = new Request.Builder().url(url).get();
        updateHead(builder, content);
        Request request = builder.build();
        final Call call = _client.newCall(request);
        asyncRequest(call, flag, notify);
        if (CommonDef.s_debug)
        {
            Logger.showDebugMsg("okhttp", "url=" + url + " flag=" + flag + " send " + url);
        }
    }

    private void updateHead(Request.Builder builder, String content)
    {
        try
        {
            JSONObject jsonObj = new JSONObject(content);
            if (!jsonObj.has("head"))
            {
                return;
            }
            jsonObj = jsonObj.getJSONObject("head");
            Iterator<String> keys = jsonObj.keys();
            while (keys.hasNext())
            {
                String key = keys.next();
                String value = jsonObj.getString(key);
                builder.header(key, value);
            }
//            RequestBody formBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonObj.getString("content"));
//            builder.post(formBody);
        }
        catch (Exception e)
        {
            Logger.errorMsg(e.getMessage());
        }
    }

    //    public void httpGetFile(final String url, String head ,final String fileName, final NetworkNotify notify)
    //    {
    //        String flag = Md5.string2MD5(url);
    //        if (_fileList.containsKey(flag))
    //        {
    //            _fileList.get(flag).add(notify);
    //        }
    //        else
    //        {
    //            List<NetworkNotify> list = new ArrayList<>();
    //            list.add(notify);
    //            _fileList.put(flag, list);
    //            Request.Builder builder = new Request.Builder().url(url).get();
    //            if (!StringUtils.isNullOrEmpty(head))
    //            {
    //                try
    //                {
    //                    JSONObject jsonObj = new JSONObject(head);
    //                    Iterator<String> keys = jsonObj.keys();
    //                    while (keys.hasNext())
    //                    {
    //                        String key = keys.next();
    //                        String value = jsonObj.getString(key);
    //                        builder.header(key, value);
    //                    }
    //                }
    //                catch (Exception e)
    //                {
    //                    Logger.errorMsg(e.getMessage());
    //                }
    //            }
    //
    //            Request request = builder.build();
    //            final Call call = _client.newCall(request);
    //            asyncRequest(call, flag,notify, fileName);
    //        }
    //
    //        if (CommonDef.s_debug)
    //        {
    //            Logger.showDebugMsg("nativeclient", "flag=" + flag + " send " + url);
    //        }
    //    }

    public void httpGetFile(final String url, String head, final String fileName, final NetworkNotify notify)
    {
        //这个是ui线程回调，可直接操作UI
        final UIProgressListener uiProgressRequestListener = new UIProgressListener()
        {
            @Override
            public void onUIProgress(long bytesWrite, long contentLength, boolean done)
            {
                String bit = (100 * bytesWrite) / contentLength + " %";
                notify.onSuccess(NativeClient.s_httpProcess, bit.getBytes());
            }

            @Override
            public void onUIStart(long bytesWrite, long contentLength, boolean done)
            {
                super.onUIStart(bytesWrite, contentLength, done);
            }

            @Override
            public void onUIFinish(long bytesWrite, long contentLength, boolean done)
            {
                super.onUIFinish(bytesWrite, contentLength, done);
            }
        };

        String flag = Md5.string2MD5(url);
        if (_fileList.containsKey(flag))
        {
            _fileList.get(flag).add(notify);
        }
        else
        {
            List<NetworkNotify> list = new ArrayList<>();
            list.add(notify);
            _fileList.put(flag, list);
            Request.Builder builder = new Request.Builder().url(url).get();
            if (!StringUtils.isNullOrEmpty(head))
            {
                try
                {
                    JSONObject jsonObj = new JSONObject(head);
                    Iterator<String> keys = jsonObj.keys();
                    while (keys.hasNext())
                    {
                        String key = keys.next();
                        String value = jsonObj.getString(key);
                        builder.header(key, value);
                    }
                }
                catch (Exception e)
                {
                    Logger.errorMsg(e.getMessage());
                }
            }

            Request request = builder.build();
            OkHttpClient client = ProgressHelper.addProgressResponseListener(_client, uiProgressRequestListener);
            final Call call = client.newCall(request);
            asyncRequest(call, flag, notify, fileName);
        }

        if (CommonDef.s_debug)
        {
            Logger.showDebugMsg("nativeclient", "flag=" + flag + " send " + url);
        }
    }

    public void httpUploadFile(final String url, String head, RequestBody requestBody, final NetworkNotify notify)
    {
        final String flag = NetworkManager.getFlags();
        _notifyList.put(flag, notify);

        //这个是ui线程回调，可直接操作UI
        final UIProgressListener uiProgressRequestListener = new UIProgressListener()
        {
            @Override
            public void onUIProgress(long bytesWrite, long contentLength, boolean done)
            {
//                Log.e("TAG", (100 * bytesWrite) / contentLength + " % done ");
                String bit = (100 * bytesWrite) / contentLength + " %";
                notify.onSuccess(NativeClient.s_httpProcess, bit.getBytes());
            }

            @Override
            public void onUIStart(long bytesWrite, long contentLength, boolean done)
            {
                super.onUIStart(bytesWrite, contentLength, done);
            }

            @Override
            public void onUIFinish(long bytesWrite, long contentLength, boolean done)
            {
                super.onUIFinish(bytesWrite, contentLength, done);
            }
        };

        //进行包装，使其支持进度回调
        Request.Builder builder = new Request.Builder().url(url);
        try
        {
            JSONObject jsonObj = new JSONObject(head);
            Iterator<String> keys = jsonObj.keys();
            while (keys.hasNext())
            {
                String key = keys.next();
                String value = jsonObj.getString(key);
                builder.header(key, value);
            }
        }
        catch (Exception e)
        {
            Logger.errorMsg(e.getMessage());
        }
        final Request request = builder.post(ProgressHelper.addProgressRequestListener(requestBody, uiProgressRequestListener)).build();
        //        final Request request = builder.post(requestBody).build();
        //开始请求
        _client.newCall(request).enqueue(new Callback()
        {
            @Override
            public void onFailure(Call call, IOException e)
            {
                Http_result obj = new Http_result();
                obj._flag = flag;
                if (e != null && !StringUtils.isNullOrEmpty(e.getMessage()))
                {
                    obj._data = e.getMessage().getBytes();
                }
                else if (call != null && call.request() != null)
                {
                    obj._data = call.request().toString().getBytes();
                }
                else
                {
                    obj._data = "unknow data".getBytes();
                }
                Message msg = Message.obtain();
                msg.obj = obj;
                msg.arg1 = NativeClient.s_noNetwork;
                _handler.sendMessage(msg);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException
            {
//                Headers head = response.networkResponse().request().headers();
//                int len = head.size();
//                for (int i = 0; i < len; i++)
//                {
//                    String key = head.name(i);
//                    String val = head.value(i);
//                    Logger.showDebugMsg(key + ":" + val);
//                }
                if (response.isSuccessful())
                {
                    Http_result obj = new Http_result();
                    obj._flag = flag;
                    obj._data = response.body().bytes();
                    Message msg = Message.obtain();
                    msg.obj = obj;
                    msg.arg1 = NativeClient.s_httpSuccess;
                    _handler.sendMessage(msg);
                }
                else
                {
                    Http_result obj = new Http_result();
                    obj._flag = flag;
                    obj._data = response.body().bytes();
                    Message msg = Message.obtain();
                    msg.obj = obj;
                    msg.arg1 = NativeClient.s_httpError;
                    _handler.sendMessage(msg);
                }
                //				notify.onSuccess(0,response.body().bytes());
            }
        });
    }

    public void httpUploadFile(final String url, final String name, final String fileName, final NetworkNotify notify)
    {
        final String flag = NetworkManager.getFlags();
        _notifyList.put(flag, notify);

        File file = new File(fileName);
        if (!file.exists())
        {
            return;
        }
        //这个是ui线程回调，可直接操作UI
        final UIProgressListener uiProgressRequestListener = new UIProgressListener()
        {
            @Override
            public void onUIProgress(long bytesWrite, long contentLength, boolean done)
            {
                //				Log.e("TAG", "bytesWrite:" + bytesWrite);
                //				Log.e("TAG", "contentLength" + contentLength);
                //				Log.e("TAG", (100 * bytesWrite) / contentLength + " % done ");
                //				Log.e("TAG", "done:" + done);
                //				Log.e("TAG", "================================");
                //notify.onSuccess(0,"ok".getBytes());
                //ui层回调
                //uploadProgress.setProgress((int) ((100 * bytesWrite) / contentLength));
                //Toast.makeText(getApplicationContext(), bytesWrite + " " + contentLength + " " + done, Toast.LENGTH_LONG).show();
            }

            @Override
            public void onUIStart(long bytesWrite, long contentLength, boolean done)
            {
                super.onUIStart(bytesWrite, contentLength, done);
                //Toast.makeText(getApplicationContext(),"start",Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onUIFinish(long bytesWrite, long contentLength, boolean done)
            {
                super.onUIFinish(bytesWrite, contentLength, done);
                //Toast.makeText(getApplicationContext(),"end",Toast.LENGTH_SHORT).show();
            }
        };

        //构造上传请求，类似web表单
        RequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("filename", name)
                //										  .addFormDataPart("photo", file.getName(), RequestBody.create(null, file))
                .addPart(Headers.of("Content-Disposition", "form-data; name=\"userfile\";filename=\"" + file.getName() + "\""), RequestBody.create(MediaType.parse("application/octet-stream"), file)).build();
        //进行包装，使其支持进度回调
        final Request request = new Request.Builder().url(url).post(ProgressHelper.addProgressRequestListener(requestBody, uiProgressRequestListener)).build();
        //开始请求
        _client.newCall(request).enqueue(new Callback()
        {
            @Override
            public void onFailure(Call call, IOException e)
            {
                Http_result obj = new Http_result();
                obj._flag = flag;
                if (e != null && !StringUtils.isNullOrEmpty(e.getMessage()))
                {
                    obj._data = e.getMessage().getBytes();
                }
                else if (call != null && call.request() != null)
                {
                    obj._data = call.request().toString().getBytes();
                }
                else
                {
                    obj._data = "unknow data".getBytes();
                }
                Message msg = Message.obtain();
                msg.obj = obj;
                msg.arg1 = NativeClient.s_noNetwork;
                _handler.sendMessage(msg);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException
            {
                if (response.isSuccessful())
                {
                    Http_result obj = new Http_result();
                    obj._flag = flag;
                    obj._data = response.body().bytes();
                    Message msg = Message.obtain();
                    msg.obj = obj;
                    msg.arg1 = NativeClient.s_httpSuccess;
                    _handler.sendMessage(msg);
                }
                else
                {
                    Http_result obj = new Http_result();
                    obj._flag = flag;
                    obj._data = response.body().bytes();
                    Message msg = Message.obtain();
                    msg.obj = obj;
                    msg.arg1 = NativeClient.s_httpError;
                    _handler.sendMessage(msg);
                }
                //				notify.onSuccess(0,response.body().bytes());
            }
        });
    }

    private void asyncRequest(final Call call, final String flag, final NetworkNotify notify)
    {
        call.enqueue(new Callback()
        {
            @Override
            public void onFailure(Call call, IOException e)
            {
                Http_result obj = new Http_result();
                obj._flag = flag;
                if (e != null && !StringUtils.isNullOrEmpty(e.getMessage()))
                {
                    obj._data = e.getMessage().getBytes();
                }
                else if (call != null && call.request() != null)
                {
                    obj._data = call.request().toString().getBytes();
                }
                else
                {
                    obj._data = "unknow data".getBytes();
                }
                Message msg = Message.obtain();
                msg.obj = obj;
                msg.arg1 = NativeClient.s_noNetwork;
                _handler.sendMessage(msg);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException
            {
                if (response.isSuccessful())
                {
                    byte[] ret = response.body().bytes();
                    if (notify.onThreadData(NativeClient.s_httpSuccess, ret))
                    {
                        Http_result obj = new Http_result();
                        obj._flag = flag;
                        obj._data = ret;
                        Message msg = Message.obtain();
                        msg.obj = obj;
                        msg.arg1 = NativeClient.s_httpSuccess;
                        _handler.sendMessage(msg);
                    }
                    else
                    {
                        if (_notifyList.containsKey(flag))
                        {
                            NetworkNotify info = _notifyList.get(flag);
                            if (info == null)
                            {
                                Logger.showDebugMsg(Logger.s_error, "Exception", "NetworkNotify not find");
                                return;
                            }
                            _notifyList.remove(flag);
                            Logger.showDebugMsg("remove flag " + flag);
                        }
                        else if (_fileList.containsKey(flag))
                        {
                            List<NetworkNotify> list = _fileList.get(flag);
                            _fileList.remove(flag);
                            Logger.showDebugMsg("remove flag " + flag);
                        }
                    }
                }
                else
                {
                    Http_result obj = new Http_result();
                    obj._flag = flag;
                    obj._data = response.body().bytes();
                    Message msg = Message.obtain();
                    msg.obj = obj;
                    msg.arg1 = NativeClient.s_httpError;
                    _handler.sendMessage(msg);
                }
            }
        });
    }

    private void asyncRequest(final Call call, final String flag, final NetworkNotify notify, final String fileName)
    {
        call.enqueue(new Callback()
        {
            @Override
            public void onFailure(Call call, IOException e)
            {
                Http_result obj = new Http_result();
                obj._flag = flag;
                if (e != null && !StringUtils.isNullOrEmpty(e.getMessage()))
                {
                    obj._data = e.getMessage().getBytes();
                }
                else if (call != null && call.request() != null)
                {
                    obj._data = call.request().toString().getBytes();
                }
                else
                {
                    obj._data = "unknow data".getBytes();
                }
                Message msg = Message.obtain();
                msg.obj = obj;
                msg.arg1 = NativeClient.s_noNetwork;
                _handler.sendMessage(msg);
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException
            {
                if (response.isSuccessful())
                {
                    InputStream is = null;
                    byte[] buf = new byte[2048];
                    int len = 0;
                    FileOutputStream fos = null;
                    try
                    {
                        is = response.body().byteStream();
                        File file = new File(fileName);
                        if(!file.exists())
                        {
                            file.createNewFile();
                        }
                        fos = new FileOutputStream(file);
                        while ((len = is.read(buf)) != -1)
                        {
                            fos.write(buf, 0, len);
                        }
                        fos.flush();
                        Http_result obj = new Http_result();
                        obj._flag = flag;
                        obj._data = null;
                        Message msg = Message.obtain();
                        msg.obj = obj;
                        msg.arg1 = NativeClient.s_httpSuccess;
                        _handler.sendMessage(msg);
                    }
                    catch (IOException e)
                    {
                        Http_result obj = new Http_result();
                        obj._flag = flag;
                        obj._data = "network disconnect".getBytes();
                        Message msg = Message.obtain();
                        msg.obj = obj;
                        msg.arg1 = NativeClient.s_httpError;
                        _handler.sendMessage(msg);
                        Logger.errorMsg(e.getMessage());
                    }
                    finally
                    {
                        try
                        {
                            if (is != null)
                                is.close();
                            if (fos != null)
                                fos.close();
                        }
                        catch (IOException e)
                        {
                            Logger.errorMsg(e.getMessage());
                        }
                    }
                }
                else
                {
                    Http_result obj = new Http_result();
                    obj._flag = flag;
                    obj._data = response.body().bytes();
                    Message msg = Message.obtain();
                    msg.obj = obj;
                    msg.arg1 = NativeClient.s_noNetwork;
                    _handler.sendMessage(msg);
                }
            }
        });
    }
}
